/**
 * ZTMZ Pacenote Dashboard - Web Client
 * Receives drawing commands from server and renders them on canvas
 * Server executes Lua dashboards and sends rendered commands
 * VERSION: 2025-10-19-v2 (transparent fix)
 */

console.log('Dashboard Client VERSION: 2025-10-19-v2 (transparent fix)');

// ============================================================================
// Global State
// ============================================================================
const state = {
    ws: null,
    dashboards: [],
    imageCache: {},
    isRunning: false,
    canvas: null,
    ctx: null
};

// ============================================================================
// Initialization
// ============================================================================
window.addEventListener('DOMContentLoaded', async () => {
    updateLoadingText('Initializing canvas...');
    showCanvas();
    
    updateLoadingText('Fetching dashboard list...');
    await loadDashboardList();
    
    hideLoading();
    updateLoadingText('Connecting to server...');
    connectWebSocket();
});

// ============================================================================
// Dashboard Loading
// ============================================================================
async function loadDashboardList() {
    try {
        const response = await fetch('/api/dashboards');
        if (!response.ok) throw new Error('Failed to fetch dashboards');
        
        state.dashboards = await response.json();
        renderDashboardList();
        
        // Preload images for all enabled dashboards
        await loadDashboardImages();
    } catch (error) {
        showError('Failed to load dashboard list: ' + error.message);
        console.error(error);
    }
}

function renderDashboardList() {
    const listContainer = document.getElementById('dashboard-list');
    listContainer.innerHTML = '';
    
    const enabledCount = state.dashboards.filter(d => d.enabled).length;
    const card = document.createElement('div');
    card.className = 'dashboard-card';
    card.innerHTML = `
        <h3>Web Dashboard View</h3>
        <p><strong>${enabledCount} dashboard(s) enabled</strong></p>
        <p>Dashboards are rendered server-side and streamed to your browser.</p>
        <p>The server will automatically render all enabled dashboards.</p>
    `;
    listContainer.appendChild(card);
}

async function loadDashboardImages() {
    // Load all dashboard images for all enabled dashboards
    for (const dashboardInfo of state.dashboards.filter(d => d.enabled)) {
        try {
            const response = await fetch(`/api/dashboard/${dashboardInfo.id}`);
            if (!response.ok) continue;
            
            const dashboard = await response.json();
            
            // Preload all images
            const imagePromises = Object.entries(dashboard.imageResources).map(([key, url]) => {
                return new Promise((resolve) => {
                    const img = new Image();
                    img.onload = () => {
                        state.imageCache[key] = img;
                        console.log('Loaded image:', key, 'from', url);
                        resolve();
                    };
                    img.onerror = () => {
                        console.warn(`Failed to load image: ${key} from ${url}`);
                        resolve();
                    };
                    img.src = url;
                });
            });
            
            await Promise.all(imagePromises);
        } catch (error) {
            console.error(`Failed to load images for dashboard ${dashboardInfo.name}:`, error);
        }
    }
}

// ============================================================================
// Drawing Command Player
// ============================================================================

/**
 * Execute a single drawing command on the canvas
 */
function executeDrawingCommand(cmd) {
    const ctx = state.ctx;
    
    try {
        switch (cmd.type) {
        case 'clear':
            const clearAlpha = cmd.color ? (cmd.color >> 24) & 0xFF : 0;
            if (cmd.color) {
                ctx.fillStyle = colorToCSS(cmd.color);
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            } else {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }
            break;
            
        case 'drawLine':
            ctx.save();
            ctx.strokeStyle = colorToCSS(cmd.color);
            ctx.lineWidth = cmd.thickness;
            ctx.beginPath();
            ctx.moveTo(cmd.x1, cmd.y1);
            ctx.lineTo(cmd.x2, cmd.y2);
            ctx.stroke();
            ctx.restore();
            break;
            
        case 'drawRectangle':
            ctx.save();
            ctx.strokeStyle = colorToCSS(cmd.color);
            ctx.lineWidth = cmd.thickness;
            ctx.strokeRect(cmd.x, cmd.y, cmd.width, cmd.height);
            ctx.restore();
            break;
            
        case 'fillRect':
            if (cmd.gradient) {
                // Create gradient based on type
                let gradient;
                if (cmd.gradient.type === 'linear') {
                    gradient = ctx.createLinearGradient(
                        cmd.gradient.x0, cmd.gradient.y0,
                        cmd.gradient.x1, cmd.gradient.y1
                    );
                } else if (cmd.gradient.type === 'radial') {
                    gradient = ctx.createRadialGradient(
                        cmd.gradient.x0, cmd.gradient.y0, cmd.gradient.r0,
                        cmd.gradient.x1, cmd.gradient.y1, cmd.gradient.r1
                    );
                }
                
                if (gradient && cmd.gradient.colors && cmd.gradient.colors.length > 0) {
                    // Add color stops
                    const colorCount = cmd.gradient.colors.length;
                    for (let i = 0; i < colorCount; i++) {
                        const position = colorCount > 1 ? i / (colorCount - 1) : 0;
                        gradient.addColorStop(position, colorToCSS(cmd.gradient.colors[i]));
                    }
                    ctx.fillStyle = gradient;
                } else {
                    ctx.fillStyle = colorToCSS(cmd.color);
                }
                ctx.fillRect(cmd.x, cmd.y, cmd.width, cmd.height);
            } else {
                // Skip fully transparent fills (they clear underlying content)
                const alpha = (cmd.color >> 24) & 0xFF;
                if (alpha > 0) {
                    ctx.fillStyle = colorToCSS(cmd.color);
                    ctx.fillRect(cmd.x, cmd.y, cmd.width, cmd.height);
                }
            }
            ctx.restore();
            break;
            
        case 'drawCircle':
            ctx.save();
            ctx.strokeStyle = colorToCSS(cmd.color);
            ctx.lineWidth = cmd.thickness;
            ctx.beginPath();
            ctx.arc(cmd.x, cmd.y, cmd.radius, 0, Math.PI * 2);
            ctx.stroke();
            ctx.restore();
            break;
            
        case 'fillCircle':
            ctx.save();
            ctx.fillStyle = colorToCSS(cmd.color);
            ctx.beginPath();
            ctx.arc(cmd.x, cmd.y, cmd.radius, 0, Math.PI * 2);
            ctx.fill();
            ctx.restore();
            break;
            
        case 'drawText':
            ctx.fillStyle = colorToCSS(cmd.color);
            
        case 'drawTextWithBackground':
            ctx.save();
            ctx.font = `${cmd.fontSize}px ${cmd.fontName}`;
            
            // Handle multi-line text
            const bgLines = cmd.text.split('\n');
            const lineHeight = cmd.fontSize * 1.2;
            
            // Calculate total text dimensions
            let maxWidth = 0;
            for (const line of bgLines) {
                const metrics = ctx.measureText(line);
                maxWidth = Math.max(maxWidth, metrics.width);
            }
            const totalHeight = bgLines.length * lineHeight;
            
            // Draw background (skip if fully transparent)
            const bgAlpha = ((cmd.bgColor >> 24) & 0xFF);
            if (bgAlpha > 0) {
                ctx.fillStyle = colorToCSS(cmd.bgColor);
                ctx.fillRect(cmd.x, cmd.y, maxWidth, totalHeight);
            }
            
            // Draw text lines
            ctx.fillStyle = colorToCSS(cmd.color);
            ctx.textBaseline = 'top';
            for (let i = 0; i < bgLines.length; i++) {
                ctx.fillText(bgLines[i], cmd.x, cmd.y + (i * lineHeight));
            }
            
            ctx.restore();
            break;
            
        case 'drawImage':
            const img = state.imageCache[cmd.imageKey];
            if (img) {
                if (cmd.width && cmd.height) {
                    ctx.drawImage(img, cmd.x, cmd.y, cmd.width, cmd.height);
                } else {
                    ctx.drawImage(img, cmd.x, cmd.y);
                }
            } else {
                console.warn('DrawImage: Image not found in cache:', cmd.imageKey);
            }
            break;
            
        case 'drawImageRegion':
            const imgRegion = state.imageCache[cmd.imageKey];
            if (imgRegion) {
                ctx.drawImage(imgRegion, 
                    cmd.srcX, cmd.srcY, cmd.srcWidth, cmd.srcHeight,
                    cmd.destX, cmd.destY, cmd.destWidth, cmd.destHeight);
            }
            break;
            
        case 'rotate':
            ctx.rotate(cmd.angle);
            break;
            
        case 'rotateAt':
            ctx.translate(cmd.centerX, cmd.centerY);
            ctx.rotate(cmd.angle);
            ctx.translate(-cmd.centerX, -cmd.centerY);
            break;
            
        case 'translate':
            ctx.translate(cmd.x, cmd.y);
            break;
            
        case 'scale':
            ctx.scale(cmd.x, cmd.y);
            break;
            
        case 'resetTransform':
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            break;
            
        case 'save':
            ctx.save();
            break;
            
        case 'restore':
            ctx.restore();
            break;
            
        case 'setClip':
            ctx.save();
            ctx.beginPath();
            ctx.rect(cmd.x, cmd.y, cmd.width, cmd.height);
            ctx.clip();
            break;
            
        case 'releaseClip':
            ctx.restore();
            break;
        
        case 'fillGeometry':
            if (cmd.points && cmd.points.length > 0) {
                ctx.save();
                ctx.fillStyle = colorToCSS(cmd.color);
                ctx.beginPath();
                // Points are flattened: [x1, y1, x2, y2, ...]
                ctx.moveTo(cmd.points[0], cmd.points[1]);
                for (let i = 2; i < cmd.points.length; i += 2) {
                    ctx.lineTo(cmd.points[i], cmd.points[i + 1]);
                }
                // Don't call closePath() - the geometry is already closed by the arc points
                // ctx.closePath();  
                ctx.fill();
                ctx.restore();
            } else {
                console.warn('fillGeometry: No points or empty points array!');
            }
            break;
        
        case 'drawGeometry':
            if (cmd.points && cmd.points.length > 0) {
                ctx.save();
                ctx.strokeStyle = colorToCSS(cmd.color);
                ctx.lineWidth = cmd.thickness;
                ctx.beginPath();
                // Points are flattened: [x1, y1, x2, y2, ...]
                ctx.moveTo(cmd.points[0], cmd.points[1]);
                for (let i = 2; i < cmd.points.length; i += 2) {
                    ctx.lineTo(cmd.points[i], cmd.points[i + 1]);
                }
                ctx.closePath();
                ctx.stroke();
                ctx.restore();
            }
            break;
        
        default:
            console.warn('Unknown command type:', cmd.type, cmd);
            break;
        }
    } catch (error) {
        console.error('Error executing command:', cmd.type, cmd, error);
    }
}

function colorToCSS(color) {
    // Convert integer ARGB to CSS rgba
    const a = ((color >> 24) & 0xFF) / 255;
    const r = (color >> 16) & 0xFF;
    const g = (color >> 8) & 0xFF;
    const b = color & 0xFF;
    return `rgba(${r}, ${g}, ${b}, ${a})`;
}

// ============================================================================
// WebSocket Connection
// ============================================================================
function connectWebSocket() {
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const wsUrl = `${protocol}//${window.location.host}/ws`;
    
    updateConnectionStatus('Connecting...', false);
    
    state.ws = new WebSocket(wsUrl);
    
    state.ws.onopen = () => {
        console.log('WebSocket connected');
        updateConnectionStatus('Connected', true);
        state.isRunning = true;
        // No render loop needed - commands come via WebSocket messages
    };
    
    state.ws.onmessage = (event) => {
        try {
            const message = JSON.parse(event.data);
            
            if (message.type === 'drawCommands') {
                // Received drawing commands from server - render them immediately
                const commands = message.commands;
                
                for (const cmd of commands) {
                    executeDrawingCommand(cmd);
                }
            } else {
                // Ignore unknown message types
            }
        } catch (error) {
            console.error('WebSocket message error:', error);
        }
    };
    
    state.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        updateConnectionStatus('Error', false);
    };
    
    state.ws.onclose = () => {
        console.log('WebSocket closed');
        updateConnectionStatus('Disconnected', false);
        state.isRunning = false;
        
        // Auto-reconnect after 3 seconds
        setTimeout(() => {
            if (!state.ws || state.ws.readyState === WebSocket.CLOSED) {
                connectWebSocket();
            }
        }, 3000);
    };
}

// ============================================================================
// UI Controls
// ============================================================================
function showLoading() {
    document.getElementById('loading').style.display = 'flex';
}

function hideLoading() {
    document.getElementById('loading').style.display = 'none';
}

function updateLoadingText(text) {
    document.getElementById('loading-text').textContent = text;
}

function showDashboardSelector() {
    document.getElementById('dashboard-selector').classList.add('active');
    document.getElementById('menu-button').classList.remove('active');
    document.getElementById('connection-status').classList.remove('active');
}

function hideDashboardSelector() {
    document.getElementById('dashboard-selector').classList.remove('active');
}

function showCanvas() {
    const container = document.getElementById('canvas-container');
    const canvas = document.getElementById('dashboard-canvas');
    
    // Set canvas RENDERING size to match server (1920x1080)
    // This is the actual pixel resolution for drawing
    canvas.width = 1920;
    canvas.height = 1080;
    
    // Scale canvas to fit browser window using CSS
    // Calculate aspect-ratio preserving scaling
    const windowAspect = window.innerWidth / window.innerHeight;
    const canvasAspect = 1920 / 1080;
    
    if (windowAspect > canvasAspect) {
        // Window is wider - fit to height
        canvas.style.height = '100%';
        canvas.style.width = 'auto';
    } else {
        // Window is taller - fit to width
        canvas.style.width = '100%';
        canvas.style.height = 'auto';
    }
    
    console.log(`Canvas rendering size: ${canvas.width}x${canvas.height}, display size: ${canvas.clientWidth}x${canvas.clientHeight}`);
    
    state.canvas = canvas;
    state.ctx = canvas.getContext('2d');
    
    container.classList.add('active');
    document.getElementById('connection-status').classList.add('active');
}

function updateConnectionStatus(text, connected) {
    const status = document.getElementById('connection-status');
    status.textContent = text;
    status.className = connected ? 'active connected' : 'active disconnected';
}

function showError(message) {
    const errorDiv = document.getElementById('error-message');
    errorDiv.textContent = message;
    errorDiv.classList.add('active');
    
    setTimeout(() => {
        errorDiv.classList.remove('active');
    }, 5000);
}

// Handle window resize
window.addEventListener('resize', () => {
    if (state.canvas) {
        // Keep rendering size fixed at 1920x1080, just rescale CSS
        const windowAspect = window.innerWidth / window.innerHeight;
        const canvasAspect = 1920 / 1080;
        
        if (windowAspect > canvasAspect) {
            state.canvas.style.height = '100%';
            state.canvas.style.width = 'auto';
        } else {
            state.canvas.style.width = '100%';
            state.canvas.style.height = 'auto';
        }
    }
});

// Prevent context menu on long press (mobile)
window.addEventListener('contextmenu', (e) => e.preventDefault());
